package fi;

import java.io.IOException;
import java.io.InputStream;
import shaders.ShaderLoader;
import bloom.Bloom;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.badlogic.gdx.graphics.FPSLogger;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Mesh;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g3d.loaders.obj.ObjLoader;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.Matrix3;

public class PreLightPassDemo implements ApplicationListener {

	public static void main(String[] argv) {
		LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
		config.title = "prelightpass";
		config.width = 800;
		config.height = 480;
		config.useCPUSynch = false;
		config.useGL20 = true;
		config.resizable = false;
		config.depth = 16;
		config.samples = 0;
		config.vSyncEnabled = false;
		config.fullscreen = false;
		new LwjglApplication(new PreLightPassDemo(), config);
	}

	static final float QUALITY = 1.00f;
	PerspectiveCamController camController;
	PerspectiveCamera cam;

	private Mesh mesh;
	private Mesh mesh2;
	private Texture texture;
	private Texture texture2;
	private GFrameBuffer gBuffer;

	private final FPSLogger logger = new FPSLogger();

	private Mesh pointLightSphere;
	private LightHandler lightHandler;
	private ShaderProgram gBufferShader;
	private ShaderProgram secondPass;
	private Matrix3 normalMatrix = new Matrix3();
	private Bloom bloom;
	private GFrameBuffer lightBuffer;

	public void render() {
		logger.log();

		final float delta = Gdx.graphics.getDeltaTime();
		camController.update(delta);
		Gdx.gl.glEnable(GL10.GL_CULL_FACE);
		Gdx.gl.glCullFace(GL10.GL_BACK);
		Gdx.gl.glEnable(GL10.GL_DEPTH_TEST);
		Gdx.gl.glDepthFunc(GL10.GL_LESS);
		Gdx.gl.glDepthMask(true);

		normalMatrix.set(cam.view);

		// fill the g-buffer
		gBuffer.begin();
		gBufferShader.begin();
		{
			Gdx.gl.glClearColor(1, 1, 0, 0);

			Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
			gBufferShader
					.setUniformMatrix("u_normalsToView", normalMatrix, false);
			gBufferShader.setUniformMatrix("u_projectionViewMatrix",
					cam.combined, false);
			gBufferShader.setUniformMatrix("u_viewMatrix", cam.view, false);
			gBufferShader.setUniformf("invFar", 1 / cam.far);
			gBufferShader
					.setUniformf("camPos", cam.position.x, cam.position.y,
							cam.position.z, 1.0f / cam.far);
			mesh.render(gBufferShader, GL10.GL_TRIANGLES);
			mesh2.render(gBufferShader, GL10.GL_TRIANGLES);

		}
		gBufferShader.end();
		gBuffer.end();

		// accumulate all point lights
		gBuffer.getColorBufferTexture().bind(0);
		lightHandler.render(cam);

		Gdx.gl.glDepthMask(true);
		Gdx.gl.glEnable(GL10.GL_CULL_FACE);
		Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
		Gdx.gl.glDepthFunc(GL10.GL_LEQUAL);

		// color
		bloom.capture();
		secondPass.begin();
		{

			secondPass.setUniformi("u_texture1", 1);
			secondPass.setUniformMatrix("u_projectionViewMatrix",
					cam.combined, false);
			secondPass
					.setUniformMatrix("u_viewMatrix", cam.view, false);
			lightBuffer.getColorBufferTexture().bind(1);
			texture.bind(0);
			mesh.render(secondPass, GL10.GL_TRIANGLES);
			texture2.bind();
			mesh2.render(secondPass, GL10.GL_TRIANGLES);

		}
		secondPass.end();
		bloom.render();

	}

	public void create() {
		int w = (int) ((float) Gdx.graphics.getWidth() * QUALITY);
		int h = (int) ((float) Gdx.graphics.getHeight() * QUALITY);

		gBuffer = new GFrameBuffer(Format.RGBA8888, w, h, true);
		gBuffer.getColorBufferTexture().setFilter(TextureFilter.Nearest,
				TextureFilter.Nearest);

		lightBuffer = new GFrameBuffer(Format.RGBA8888, w, h, true);

		Quad.createFullScreenQuad();
		
		// bloomlib make this too easy
		bloom = new Bloom(Gdx.graphics.getWidth() / 2,
				Gdx.graphics.getHeight() / 2, true, false, true);
		bloom.setTreshold(0.8f);
		bloom.blurPasses=1;
		bloom.setOriginalIntesity(1f);
		bloom.setBloomIntesity(2.17f);

		cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(),
				Gdx.graphics.getHeight());
		cam.near = 0.5f;
		cam.far = 128;
		cam.position.set(0, 0.5f, 1.5f);
		cam.update();

		camController = new PerspectiveCamController(cam);
		Gdx.input.setInputProcessor(camController);

		texture = new Texture(Gdx.files.internal("data/texture.png"),
				Format.RGB565,
				true);
		texture.setFilter(TextureFilter.MipMapLinearNearest,
				TextureFilter.Linear);

		texture2 = new Texture(Gdx.files.internal("data/texture2.png"),
				Format.RGB565,
				true);
		texture2.setFilter(TextureFilter.MipMapLinearNearest,
				TextureFilter.Linear);

		try {
			InputStream in = Gdx.files.internal("data/ship.obj").read();
			mesh = ObjLoader.loadObj(in);
			in.close();
			in = Gdx.files.internal("data/scene.obj").read();
			mesh2 = ObjLoader.loadObj(in);
			in.close();
			in = Gdx.files.internal("data/sphere.obj").read();
			pointLightSphere = ObjLoader.loadObj(in);
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		mesh2.scale(1.25f, 1.25f, 1.25f);
		pointLightSphere.scale(1.2f, 1.2f, 1.2f);

		lightHandler = new LightHandler(lightBuffer, pointLightSphere);

		gBufferShader = ShaderLoader.createShader("gBuffer", "gBuffer");
		secondPass = ShaderLoader.createShader("secondpass", "secondpass");

		

	}

	public void resize(int width, int height) {
	}

	public void pause() {
		Gdx.app.exit();
	}

	public void dispose() {
		mesh.dispose();
		mesh2.dispose();
		texture.dispose();
		texture2.dispose();
		lightHandler.dispose();
		pointLightSphere.dispose();
		gBuffer.dispose();
		System.exit(0);
	}

	public void resume() {
	}
}